{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# music21: A Toolkit for Comupter-Aided Musicology"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Some examples to test basic music21 corpus functionalities"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is a Jupyter notebook created by [@musicenfanthen](https://github.com/musicEnfanthen) and [@aWilsonandmore](https://github.com/aWilsonandmore) to work with some basic functionalities of music21 (http://web.mit.edu/music21/). For more information on Jupyter notebooks go to http://jupyter.org/. \n",
    "\n",
    "To execute a block of code in this notebook, click in the cell and press `Shift+Enter`.\n",
    "\n",
    "To get help on any music21 routine, click on it and press `Shift+Tab`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Imports and setup"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To use music21 in this notebook and python, you have to import all (\\*) routines  from music21 at first with the following command.\n",
    "\n",
    "You’ll probably get a few warnings that you’re missing some optional modules. That’s okay. If you get a warning that “no module named music21” then something probably went wrong above."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline     \n",
    "# imports the matplot library to plot graphs etc.\n",
    "\n",
    "from music21 import *"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Probably you have to set manually the correct file path to an Application that is able to open MusicXML files (like MuseScore). To do so, you can use the `music21.environment` module where you can set an `musicxmlPath` key.\n",
    "\n",
    "Make sure to change below the string `path/to/your/musicXmlApplication` with the correct file path (keep the quotation marks):\n",
    "- on Mac e.g.: `/Applications/MuseScore 2.app/Contents/MacOS/mscore` \n",
    "- or on Windows e.g.: `C:/Program Files (x86)/MuseScore 2/bin/MuseScore.exe`\n",
    "\n",
    "and uncomment the line (remove the `#` at the begin of the line).\n",
    "\n",
    "In the same way, you can also add a path to your lilypond installation, using\n",
    "`env['lilypondPath']`:\n",
    "- on Mac e.g.: `Applications/Lilypond.app`\n",
    "- on Windows e.g.: `C:/Program Files (x86)/LilyPond/usr/bin/lilypond.exe`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# definition of environment settings is different from the settings \n",
    "# when this jupyter notebook runs locally on your machine.\n",
    "# Changes are necessary because jupyter notebook is running via Binder image\n",
    "\n",
    "env = environment.Environment()\n",
    "\n",
    "env['lilypondPath']='/usr/bin/lilypond'\n",
    "env['musescoreDirectPNGPath'] = '/usr/bin/musescore'\n",
    "env['musicxmlPath'] = '/usr/bin/musescore'\n",
    "\n",
    "environment.set('pdfPath', '/usr/bin/musescore')\n",
    "environment.set('graphicsPath', '/usr/bin/musescore')\n",
    "\n",
    "print('Environment settings:')\n",
    "print('musicXML:  ', env['musicxmlPath'])\n",
    "print('musescore: ', env['musescoreDirectPNGPath'])\n",
    "print('lilypond:  ', env['lilypondPath'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-block alert-warning\">\n",
    "Using jupyter notebook inside a Binder image causes some issues with music21's \".show()\"-method (see: https://github.com/cuthbertLab/music21/issues/260). Thanks to Tony Hirst (@psychemedia) there is a small workaround with a redefinition of the method:\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# re-definition of sho()-method ---> \"HACK\" from https://github.com/psychemedia/showntell/blob/music/index_music.ipynb\n",
    "# see also this music21 issue: https://github.com/cuthbertLab/music21/issues/260\n",
    "%load_ext music21.ipython21\n",
    "\n",
    "from IPython.display import Image\n",
    "\n",
    "def render(s):\n",
    "    s.show('lily.png')\n",
    "    return Image(filename=s.write('lily.png'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Starting with corpus examples"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "List of works found in the music21 corpus: http://web.mit.edu/music21/doc/about/referenceCorpus.html#demonstration-files\n",
    "\n",
    "music21's corpus module: http://web.mit.edu/music21/doc/moduleReference/moduleCorpus.html"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "demoPaths = corpus.getComposer('demos')\n",
    "demoPaths"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "demoPath = demoPaths[0]\n",
    "\n",
    "demo = corpus.parse(demoPath)\n",
    "\n",
    "print(demo.corpusFilepath)\n",
    "#demo.show()\n",
    "render(demo)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There is a bunch of metadata bundles in the music21 corpus. You can make use of it via the corpus search:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sbBundle = corpus.search('Bach', 'composer')\n",
    "print(sbBundle)\n",
    "print(sbBundle[0])\n",
    "print(sbBundle[0].sourcePath)\n",
    "sbBundle[0].metadata.all()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Loading files & formats from corpus or disk\n",
    "\n",
    "It is also possible to load and parse various file formats directly into music21. \n",
    "\n",
    "\"In general, to load a file from disk, call music21.converter.parse(), which can handle importing all supported formats. (For complete documentation on file and data formats, see http://web.mit.edu/music21/doc/moduleReference/moduleConverter.html#moduleconverter\" (UsersGuide 08).\n",
    "\n",
    "The following example takes a musicXML-File as input. A list of possible file formats can be found here: http://web.mit.edu/music21/doc/usersGuide/usersGuide_08_installingMusicXML.html"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "s = corpus.parse('bach/bwv65.2.xml')\n",
    "# s.show()\n",
    "render(s) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Score manipulation\n",
    "\n",
    "With music21 it is pretty straightforward to manipulate different parts/voices of a score. In the following example, we take the four voice chorale setting from above and transform it into a two-part piano setting."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "fVoices = stream.Part((s.parts['Soprano'], s.parts['Alto'])).chordify()\n",
    "mVoices = stream.Part((s.parts['Tenor'], s.parts['Bass'])).chordify()\n",
    "\n",
    "chorale2p = stream.Score((fVoices, mVoices))\n",
    "# chorale2p.show()\n",
    "render(chorale2p)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Or what about a more bass-with-accompaniment-style setting?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "upperVoices = stream.Part((s.parts['Soprano'], s.parts['Alto'], s.parts['Tenor'])).chordify()\n",
    "bass = stream.Part((s.parts['Bass']))\n",
    "\n",
    "chorale3p = stream.Score((upperVoices, bass))\n",
    "# chorale3p.show()\n",
    "render(chorale3p)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To see, how music21 stores this stream internally, use the .`show()`-method:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chorale3p.show('text')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for c in chorale3p.recurse().getElementsByClass('Chord'):\n",
    "    print(c)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Roman numeral analysis\n",
    "\n",
    "music21 makes it easy to apply roman numeral analysis to chordified music. To do so, we use the `.chordify()`-method on the chorale above, grep all chords with the `getElementsByClass()`-method, bring them into closed position and apply the roman numeral as lyrics. Additionally we highlight some seventh chords:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# chordify the chorale\n",
    "choraleChords = chorale3p.chordify()\n",
    "\n",
    "for c in choraleChords.recurse().getElementsByClass('Chord'):\n",
    "    # force closed position\n",
    "    c.closedPosition(forceOctave=4, inPlace=True)\n",
    "    \n",
    "    # apply roman numerals\n",
    "    rn = roman.romanNumeralFromChord(c, key.Key('A'))\n",
    "    c.addLyric(str(rn.figure))\n",
    "    \n",
    "    # highlight dimished seventh chords\n",
    "    if c.isDiminishedSeventh():\n",
    "        c.style.color = 'red'\n",
    "    \n",
    "    # highlight dominant seventh chords\n",
    "    if c.isDominantSeventh():\n",
    "        c.style.color = 'blue'\n",
    "\n",
    "# choraleChords.show()\n",
    "render(choraleChords)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Another example (plotting)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p = corpus.parse('bach/bwv846.xml')\n",
    "# p.show()\n",
    "render(p)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p.analyze('key')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p.show('text')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "len(p.parts)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "len(p.flat.notes)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There are some plotting possibilities that come with `matplotlib`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "graph.findPlot.FORMATS"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To plot a stream, just use the `.plot()`-method instead of `-show()`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p.plot('pianoroll')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p.plot('horizontalbar')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
